//
//	File Name   :	tgaLoader.cpp
//	Description :	tgaLoader implementation file.
//	Author		:	William McVicar
//  Mail		:	mcvicar.william@gmail.com
//

//  Precompiled Header
#include "CommonDefines.h"

//  Library Includes

//  Local Includes
#include "../ProteinFramework.h"
#include "../IFileSystem.h"
#include "../Image.h"

//  This includes
#include "tgaLoader.h"

//	Macros

//  Static Variables
namespace Protein
{
u8 CTGA::uTGAcompare[12] = {0,0,2, 0,0,0,0,0,0,0,0,0};
u8 CTGA::cTGAcompare[12] = {0,0,10,0,0,0,0,0,0,0,0,0};
}

//  Static Function Prototypes

//  Implementation
namespace Protein
{

bool CTGA::Load( const c8* _szFilename, Image& _rImage )
{
	std::string path;
	char buf[ PROTEIN_MAX_PATH ];
	strcpy( buf, _szFilename );
	strcat( buf, ".tga" );
	if( IFileSystem::FILESYSTEM_ERROR_FILE_NOT_FOUND == Protein::fileSystem().FileExists( buf, path ) )
		return false;

	FILE* pFile = NULL;
	if( !fileSystem().Open( path.c_str(), g_kBINARY_READ_ONLY, &pFile, true ) )
		return false;

	TGAHeader header;

	if( !fileSystem().Read( (u8*)&header, sizeof( TGAHeader ), 1, pFile ) )
	{
		fileSystem().Close( pFile );
		return false;
	}

	if( memcmp( uTGAcompare, &header, sizeof( TGAHeader ) ) == 0 )
		return CTGA::loadUncompressed( pFile, _rImage, path );
	else if( memcmp( cTGAcompare, &header, sizeof( TGAHeader ) ) == 0 )
		return CTGA::loadCompressed( pFile, _rImage, path );
	
	fileSystem().Close( pFile );
	return false;
}

bool CTGA::loadUncompressed( FILE* file, Image& _rImage, const std::string& path )
{
	TGA tga;
	if( fileSystem().Read( tga.header,  sizeof( tga.header ), 1, file ) == 0 )
	{
		fileSystem().Close( file );
		return false;
	}

	tga.Width		= tga.header[1] * 256 + tga.header[0];				
	tga.Height		= tga.header[3] * 256 + tga.header[2];				
	tga.Bpp			= tga.header[4];									

	if((tga.Width <= 0) || (tga.Height <= 0) || ((tga.Bpp != 24) && (tga.Bpp !=32)))
	{
		fileSystem().Close( file );
		return false;														
	}

	ePixelFormat pf = PF_UNKNOWN;

	if(tga.Bpp == 24)													
		pf = PF_RGB8;											
	else																	
		pf = PF_RGBA8;											

	tga.bytesPerPixel	= tga.Bpp >> 3;//(tga.Bpp / 8);									
	tga.imageSize		= tga.bytesPerPixel * tga.Width * tga.Height;		
	u8* imageData		= new u8[tga.imageSize];					

	if(!imageData)											
	{
		fileSystem().Close( file );
		delete [] imageData;
		return false;														
	}

	if( fileSystem().Read( imageData, 1, tga.imageSize, file ) != (i32)tga.imageSize )
	{
		fileSystem().Close( file );
		delete [] imageData;
		return false;
	}

	// Byte Swapping Optimized By Steve Thomas
	for(u32 i = 0; i < tga.imageSize; i += tga.bytesPerPixel)
		imageData[i] ^= imageData[i+2] ^= imageData[i] ^= imageData[i+2];

	fileSystem().Close( file );

	_rImage.Init( tga.Width, tga.Height, 0, pf, 0, path, imageData, tga.imageSize );

	delete [] imageData;

	return true;
}

bool CTGA::loadCompressed( FILE* file, Image& _rImage, const std::string& path )
{
	TGA tga;
	if( fileSystem().Read( tga.header,  sizeof( tga.header ), 1, file ) == 0 )
	{
		fileSystem().Close( file );
		return false;
	}

	tga.Width		= tga.header[1] * 256 + tga.header[0];				
	tga.Height		= tga.header[3] * 256 + tga.header[2];				
	tga.Bpp			= tga.header[4];									

	if((tga.Width <= 0) || (tga.Height <= 0) || ((tga.Bpp != 24) && (tga.Bpp !=32)))
	{
		fileSystem().Close( file );
		return false;														
	}

	ePixelFormat pf = PF_UNKNOWN;

	if(tga.Bpp == 24)													
		pf = PF_RGB8;											
	else																	
		pf = PF_RGBA8;											

	tga.bytesPerPixel	= tga.Bpp >> 3;//(tga.Bpp / 8);									
	tga.imageSize		= tga.bytesPerPixel * tga.Width * tga.Height;		
	u8* imageData		= new u8[tga.imageSize];					

	if(!imageData)											
	{
		fileSystem().Close( file );
		delete [] imageData;
		return false;														
	}

	u32 pixelcount		= tga.Height * tga.Width;							
	u32 currentpixel	= 0;												
	u32 currentbyte		= 0;												
	u8* colourbuffer	= new u8[ tga.bytesPerPixel ];

	do
	{
		u8 chunkheader = 0;											

		if(fileSystem().Read(&chunkheader, sizeof(u8), 1, file) == 0)				
		{
			fileSystem().Close( file );
			delete [] imageData;
			delete [] colourbuffer;
			return false;
		}

		if(chunkheader < 128)												
		{																	
			++chunkheader;													
			for(short counter = 0; counter < (i16)chunkheader; counter++)		
			{
				if(fileSystem().Read(colourbuffer, 1, tga.bytesPerPixel, file) != (i32)tga.bytesPerPixel) 
				{
					fileSystem().Close( file );
					delete [] imageData;
					delete [] colourbuffer;
					return false;
				}
																						
				imageData[currentbyte  ] = colourbuffer[2];		    
				imageData[currentbyte+1] = colourbuffer[1];
				imageData[currentbyte+2] = colourbuffer[0];

				if(tga.bytesPerPixel == 4)
					imageData[currentbyte + 3] = colourbuffer[3];

				currentbyte += tga.bytesPerPixel;										
				++currentpixel;															

				if(currentpixel > pixelcount)											
				{
					fileSystem().Close( file );
					delete [] imageData;
					delete [] colourbuffer;
					return false;													
				}
			}
		}
		else																			
		{
			chunkheader -= 127;															
			if(fread(colourbuffer, 1, tga.bytesPerPixel, file) != tga.bytesPerPixel)		
			{	
				fileSystem().Close( file );
				delete [] imageData;
				delete [] colourbuffer;
				return false;
			}

			for(short counter = 0; counter < chunkheader; counter++)					
			{																			
				imageData[currentbyte  ] = colourbuffer[2];					
				imageData[currentbyte+1] = colourbuffer[1];
				imageData[currentbyte+2] = colourbuffer[0];

				if(tga.bytesPerPixel == 4)
					imageData[currentbyte + 3] = colourbuffer[3];

				currentbyte += tga.bytesPerPixel;										
				++currentpixel;															

				if(currentpixel > pixelcount)											
				{
					fileSystem().Close( file );
					delete [] imageData;
					delete [] colourbuffer;
					return false;				
				}
			}
		}
	}
	while(currentpixel < pixelcount);

	fclose(file);	

	_rImage.Init( tga.Width, tga.Height, 0, pf, 1, path, imageData, tga.imageSize );

	delete[] imageData;

	return true;
}

}